package com.zte.claa.inficombo.csa.app.service.gprscat1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.mina.core.future.WriteFuture;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zte.claa.inficombo.csa.app.config.GPRSCat1DevConnProps;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.service.csa.impl.BaseCacheManager;
import com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSDevCommand;
import com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSDevRegRsp;
import com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSMessage;
import com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSMsgHead;
import com.zte.claa.inficombo.csa.app.service.gprscat1.mina.GPRSDevConnSession;
import com.zte.claa.inficombo.csa.app.service.gprscat1.mina.GPRSDevServerManager;
import com.zte.claa.inficombo.csa.app.service.nbiot.IotLocUpdataProcessService;
import com.zte.claa.inficombo.csa.app.service.rptparse.CRC16M;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserContainer;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserGPRSCat1Updata;

@Service("gprscat1")
public class GPRSDevServiceImpl implements GPRSDevService, InitializingBean, DisposableBean {
	// 终端数据上报消息的cmd
	public static final String UPDATA_CMD = "updata";
		
	// 日志.
	private static Logger logger = LoggerFactory.getLogger("GPRSDevServiceImpl");
	
	// 连接参数.
	@Autowired
	private GPRSCat1DevConnProps connProps;
	
	@Autowired
	private BaseCacheManager cacheManager;
	
	@Autowired
	private ParserContainer parseContainer;
	
	@Autowired
	private IotLocUpdataProcessService locUpdataProcessService;
	
	// 连接管理服务器
	private GPRSDevServerManager connSvrManager = null;
	
	// 上行数据处理线程池.
	private ExecutorService dataRptExecSvr;
	
	private ParserGPRSCat1Updata parseGprsUpdate;
	
	@Override
	public void afterPropertiesSet() throws Exception {
		if(this.connProps.isSwitchon() == false){
			logger.warn("GPRSDevService is set off.");
			return;
		}
		this.parseGprsUpdate = new ParserGPRSCat1Updata();
		this.parseGprsUpdate.setParserContainer(parseContainer);
		this.parseGprsUpdate.setLocUpdataProcessService(this.locUpdataProcessService);
		try{			
			// 上行消息分发线程池.
			RejectedExecutionHandler faultHandler = new ThreadPoolExecutor.DiscardPolicy();
			// 此时是无界队列LinkedBlockingQueue，maxCoreNum参数无效...
			this.dataRptExecSvr = new ThreadPoolExecutor(this.connProps.getUpdealermaxnum(), 
				this.connProps.getUpdealermaxnum(), 120, TimeUnit.MILLISECONDS, 
    			new LinkedBlockingQueue<Runnable>(this.connProps.getMsgqueuesize()), faultHandler);
			// 初始化connserver.
			this.connSvrManager = new GPRSDevServerManager(this.connProps, this);
			this.connSvrManager.start();
			this.parseGprsUpdate.setGprsDevService(this);
			logger.info("GPRSDevService init ok!");
		}catch(Exception e){
			logger.error("GPRSDevService init failed!", e);
			this.destroy();
		}
	}
	
	               
	@Override
	public void destroy() throws Exception {
		try{
			if(this.connSvrManager != null) {
				this.connSvrManager.stop();
				this.connSvrManager = null;
			}
			if(this.dataRptExecSvr != null){
				this.dataRptExecSvr.shutdown();
				this.dataRptExecSvr = null;
			}
		}catch(Exception e){
			logger.error("destroy failed!", e);
		}
	}
	
	@Override
	public void onGPRSDevMsgReport(GPRSDevConnSession gprssession, GPRSMessage message) {
		if(this.dataRptExecSvr != null){
			int msgtp = message.getHead().getMsgtype();
			switch(msgtp){
			case GPRSMsgHead.GPRSCAT1_UP_REGREQ:
				processGPRSRegReq(gprssession,message);
				break;
			case GPRSMsgHead.GPRSCAT1_UP_HBREQ:				
			case GPRSMsgHead.GPRSCAT1_UP_INDEX:
				if(GPRSDevConnSession.DEV_REGED == gprssession.getProcstat()){
					processGPRSReport(gprssession,message);
				}else{
					logger.warn("Device is not registered!");
				}
				break;
			default:
				logger.warn("Unknow message type: tp=" + msgtp + ", msg=" + message);
				break;
			}			
		} else {
			logger.warn("Rpt message dispatcher pool is null!");
		}
		return;
	}
	
	private void processGPRSRegReq(GPRSDevConnSession gprsconn, GPRSMessage msg){
		try{
			// 注册消息 
			String deveui =  msg.getHead().getDeveui();
			String imei =  msg.getHead().getImei();
			DevBase devbase = this.cacheManager.getDev(deveui);
			GPRSDevRegRsp regRsp = new GPRSDevRegRsp();
			if(null == devbase){
				msg.setErrtype(GPRSMessage.GPRSCAT1_ERR_DEVNOTEXIST);
			}			
			if(GPRSMessage.GPRSCAT1_ERR_DEVNOTEXIST == msg.getErrtype()
			   || GPRSMessage.GPRSCAT1_ERR_AUTHFAIL == msg.getErrtype()
			   || GPRSMessage.GPRSCAT1_ERR_CRCCHECK == msg.getErrtype())
			{
				regRsp.setResult((byte)GPRSDevRegRsp.GPRSCAT1_RESULT_FAIL);
			}else{
				//重复注册也响应成功消息
				regRsp.setResult((byte)GPRSDevRegRsp.GPRSCAT1_RESULT_SUCC);
			}
			regRsp.setDeveui(deveui);
			regRsp.setImei(imei);
			regRsp.setReason((byte)msg.getErrtype());
			GPRSDevCommand gprsDevCmd = buildGPRSRegAck(regRsp);
			this.sendCommand(deveui, gprsDevCmd);
			
			if(GPRSDevRegRsp.GPRSCAT1_RESULT_SUCC != regRsp.getResult()){
				//释放session
				//this.closeSession(deveui);
				gprsconn.setProcstat(GPRSDevConnSession.DEV_UNREG);
			}else{
				gprsconn.setProcstat(GPRSDevConnSession.DEV_REGED);
				devbase.setNetworktype(DevBase.NETTYPE_GPRS);
				gprsconn.setImei(msg.getHead().getImei());
			}
		}catch(Exception e){
			logger.error("processGPRSRegReq failed: "+msg, e);
		}
	}
	
	private void processGPRSReport(GPRSDevConnSession gprsconn, GPRSMessage msg){
		try{
			if(null == msg){
				logger.error("processGPRSReport failed, msg is null!");
				return;
			}
			this.addGPRSUpdataToPool(msg);
		}catch(Exception e){
			logger.error("processGPRSReport failed: "+msg, e);
		}
	}
	
	private void addGPRSUpdataToPool(GPRSMessage msg){
		this.dataRptExecSvr.submit(new Runnable(){
			@Override
			public void run() {
				try {
					parseGprsUpdate.parseGPRSData(msg);
				} catch (Exception e) {
					logger.error("parseGPRSData failed: "+msg, e);					
				}
			}
		});
	}
	
	@Override
	public WriteFuture sendCommand(String deveui, GPRSDevCommand cmd) throws Exception {
		if(deveui == null || deveui.equals("")){
			logger.warn("GPRS sendCommand failed: deveui is null");
			throw new Exception("sendCommand failed: deveui is null!");
		}
		if(cmd == null){
			logger.warn("GPRS sendCommand failed: cmd is null");
			throw new Exception("GPRS sendCommand failed: cmd is null!");
		}
		DevBase dev = this.cacheManager.getDev(deveui);
		if(dev == null){
			logger.warn("GPRSDev not exist: "+deveui);
			throw new Exception("GPRSDev not exist: "+deveui);
		}
		GPRSDevConnSession session = this.connSvrManager.getDeveuiSessionMap().get(deveui);
		if(null == session){
			return null;
		}
		return session.sendCommand(cmd);
	}
	
	
	public static GPRSDevCommand buildGPRSRegAck(GPRSDevRegRsp regRsp){
		if(null == regRsp){
			return null;
		}
		GPRSDevCommand gprsDevCmd = new GPRSDevCommand();
		gprsDevCmd.setCmdtype(regRsp.getMsgtype());
		gprsDevCmd.setDeveui(regRsp.getDeveui());
		JSONObject regrspjson = new JSONObject(); 
		regrspjson.put("dt", GPRSMsgHead.GPRSCAT1_DW_REGRSP);
		JSONObject datajson = new JSONObject(); 
		datajson.put("deveui", regRsp.getDeveui());
		datajson.put("imei", regRsp.getImei());
		datajson.put("result", regRsp.getResult());
		datajson.put("reason", regRsp.getReason());
		int crc16code = getCrcCode(datajson.toString());
		regrspjson.put("data", datajson);
		regrspjson.put("crc", crc16code);
		String datastr = regrspjson.toString();
		byte[] payload = datastr.getBytes();
		gprsDevCmd.setPaylen(payload.length);
		gprsDevCmd.setPayload(payload);
		logger.info("buildGPRSRegAck, datastr="+datastr);
		
		String hexstrtmp = "";
		for(int i=0; i<payload.length; i++){
			byte bytetmp = payload[i];            
	        hexstrtmp += Integer.toHexString(bytetmp)+" ";
		}
		logger.info("buildGPRSRegAck, payload=" + hexstrtmp);
		return gprsDevCmd;
	}
	
	public static int getCrcCode(String srcstr) {
		if(null == srcstr || srcstr.length() == 0){
			return -1;
		}
		String hexstrtmp = "";
		for(int i=0; i<srcstr.length(); i++){
			char charstmp = srcstr.charAt(i);
            int charcode= Integer.valueOf(charstmp);
	        hexstrtmp += Integer.toHexString(charcode);
		}
		logger.info("getCrcCode srcstr data ascii str:"+hexstrtmp);		
		int crc16code = CRC16M.getCrc16CodeInt(hexstrtmp); 
		logger.info("getCrcCode crc16code:"+crc16code);
		return crc16code;
	}
	
	@Override
	public void onExceptionReport(long sessionId, Throwable cause) {
		logger.error("Exception is reported: sessionId={}", sessionId, cause);
	}


	@Override
	public void onConnectionClose(long sessionId) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void closeConnection(String deveui) throws Exception {
		this.connSvrManager.closeSessionByDeveui(deveui);
		
	}

	@Override
	public GPRSDevServerManager getConnSvrManager() {
		return connSvrManager;
	}
	
}
